package com.woldier.filesystem.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.woldier.base.exception.DoopfsException;
import com.woldier.filesystem.model.po.File;
import com.woldier.filesystem.model.po.Repository;
import com.woldier.filesystem.service.CheckService;
import com.woldier.filesystem.service.FileService;
import com.woldier.filesystem.service.RepositoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
public class CheckServiceImpl implements CheckService {

    @Autowired
    private FileService fileService;
    @Autowired
    private RepositoryService repositoryService;

    final static ExecutorService pool = Executors.newFixedThreadPool(5);

    /**
     * 获取用户仓库所有的文件信息
     *
     * @param username
     * @param repository
     * @return
     */
    @Override

    public List<File> getAll(String username, String repository) {
        /**
         *可以的改进是加入到redis
         */
        LambdaQueryWrapper<File> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(File::getNameRepo, getNameRepo(username, repository));
        return fileService.list(lambdaQueryWrapper);
    }

    /**
     * 从服务器下载
     *服务端就是根据本地发过来的文件进行校验
     *
     * 若本地文件的updateTime与服务端不一致则下载
     *
     * 若本地又该文件,服务端没有说明已经删除,告知本地删除
     *
     * 其他情形不下载.
     * @param list
     * @param username
     * @param repository
     * @return
     */

    @Override

    public Hashtable<String, List<Object>> needDownload( String username,List<File> list, String repository) throws DoopfsException {
        /**
         * 1.判断是否存在仓库,不存在则返回
         * 2.从数据库中得到仓库列表
         * 3.将服务端的list 放入map中
         * 4.遍历客户端的list
         * 4.1若存在,先将其从map中删除,然后比较时间,若local为旧时间,则请求其下载
         * 4.2若不存在,说明该本地文件已经被服务端删除,让客户端将其删除
         * 5.若map中还有数据,说明是服务端特有的新文件,让客户端下载
         */
        // 1.判断是否存在仓库,不存在则返回
        checkEmpty( username, repository);
        // 2.从数据库中得到仓库列表
        List<File> remoteList = this.getAll(username, repository);
        // 3.将服务端的list 放入map中
        Map<String, LocalDateTime> map = new Hashtable<>();
        remoteList.stream().forEach(e -> map.put(e.getPathFile(), e.getUpdateTime()));
        // 4.遍历客户端的list
        /*存储需要删除的list*/
        ArrayList<Object> removeList = new ArrayList<>();
        /*存储需要更新下载的list*/
        List<Object> downloadList = list.stream().filter(
                file -> {
                    String key = file.getPathFile();
                    LocalDateTime value = file.getUpdateTime();
                    //* 若存在,先将其从map中删除,然后比较时间,若local为旧时间,则请求其下载
                    if (map.containsKey(key)) {
                        LocalDateTime time = map.remove(key);//从map中剔除,以筛选出旨在local存在的文件
                        // 若remote时间最新,则我们将该文件加入更新列表
                        if (time.isAfter(value)) return true;
                    }
                    //* 4.2若不存在,说明该本地文件已经被服务端删除,让客户端将其删除
                    else removeList.add(file.getPathFile());
                    return false;
                }
        ).collect(Collectors.toList());
        // 5.若map中还有数据,说明是服务端特有的新文件,让客户端下载
        List<Object> res = downloadList;
        if (!map.isEmpty()) {
            map.forEach((k,v)->{
                res.add(File.builder().pathFile(k).updateTime(v).build());
            });

        }
        Hashtable<String, List<Object>> hashtable = new Hashtable<>();
        hashtable.put("download",res);
        hashtable.put("remove",removeList);
        return hashtable;
    }

    /**
     * 上传逻辑是,上传我们就以本地的为准,本地给服务端文件表
     * <p>
     * 服务端对比文件是否是最新的是最新的不用上传,不是服务端要求客户端上传;
     * <p>
     * 若本地不存在,服务器存在服务端就删除;
     * <p>
     * 如果本地存在,服务器不存在,说明是本地新建,我们要求它上传.
     *
     * @param list
     * @return
     */

    @Override

    public List<String> checkUpdate(String username, List<File> list, String repository) throws DoopfsException {
        /**
         * 1.判断该用户是否存在该仓库, 不存在直接返回
         * 2.从数据库中得到仓库列表
         * 3.将传入的list 放入map中
         * 4.遍历数据库文件列表,
         * 4.1若在map中,比较时间,时间不等则要求客户上传
         * 4.2若不在map,表示已经删除,从数据库将该文件删除,并且删除hadoop中的数据(异步).
         * 5.检查map中是否还有元素,若有表示本地新增,要求客户上传
         */
        // 1.判断该用户是否存在该仓库, 不存在直接返回
        checkEmpty(list, username, repository);
        // 2.从数据库中得到仓库列表
        List<File> remoteList = this.getAll(username, repository);
        // 3.将传入的list 放入map中
        Map<String, LocalDateTime> map = new Hashtable<>();
        list.stream().forEach(
                e -> {
                    map.put(e.getPathFile(), e.getUpdateTime());
                }
        );
        // 若远端为空表示首次上传 返回所有传入的
        if (remoteList.isEmpty()){
            return list.stream().map(File::getPathFile).collect(Collectors.toList());
        }
        //4.遍历数据库文件列表
        List<File> remainFiles = remoteList.stream().filter(
                file -> {
                    String key = file.getPathFile();
                    LocalDateTime value = file.getUpdateTime();
                    //* 4.1若在map中,比较时间,时间不等则要求客户上传
                    if (map.containsKey(key)) {
                        LocalDateTime time = map.remove(key);//从map中剔除,以筛选出旨在local存在的文件
                        // 若local时间最新,则我们将该文件加入更新列表

                        if (time.isAfter(value)) {
//                            pool.submit(
//                                    ()->{
//                                        fileService.updateById(File.builder().id(file.getId()).updateTime(time).build());
//                                    }
//                            );
                            return true;
                        }
                    }
                    //* 4.2若不在map,表示客户端不存在,从数据库删除,并且删除hdfs中保存的文件(异步线程)
                    else {
                        pool.submit(
                                () -> {
                                    fileService.removeById(file.getId());
                                    //TODO 从hdfs中删除文件
                                    //restTemplate.delete(hadoopProxy+username+"/"+key+"?op="+ HadoopOps.DELETE+"&user.name=root");
                                });

                    }
                    return false;
                }
        ).collect(Collectors.toList());
        // 5.检查map中是否还有元素,若有表示本地新增,要求客户上传
        List<String> res = remainFiles.stream().map(File::getPathFile).collect(Collectors.toList());
        if (!map.isEmpty()) {
            res.addAll(map.keySet());
        }
        return res;
    }


    /**
     * 检查仓库是否存在,不存在则创建
     *
     * @param username
     * @param repository
     * @return
     */
    @Override

    public String checkRepo(String username, String repository) throws DoopfsException {
        checkEmpty(username,repository);
        return "当前仓库存在";
    }




    /*-----------------------------------private method----------------------------------------------------------------------------*/


    private String getNameRepo(String username, String repository) {
        return username + "/" + repository;
    }

    /**
     * 检查是否为空
     * @param list
     * @param username
     * @param repository
     * @throws DoopfsException
     */
    private void checkEmpty(List<File> list, String username, String repository) throws DoopfsException {
        if (StringUtils.isEmpty(username)) DoopfsException.cast("用户名为空");
        if (StringUtils.isEmpty(repository)) DoopfsException.cast("仓库为空");
        if (list == null || list.isEmpty()) DoopfsException.cast("传入的文件列表为空");
        LambdaQueryWrapper<Repository> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Repository::getUserName,username).eq(Repository::getNameRepo,getNameRepo(username,repository));
        if (repositoryService.list(lambdaQueryWrapper).isEmpty()) DoopfsException.cast("当前仓库不存在");
    }
    private void checkEmpty( String username, String repository) throws DoopfsException {
        if (StringUtils.isEmpty(username)) DoopfsException.cast("用户名为空");
        if (StringUtils.isEmpty(repository)) DoopfsException.cast("仓库为空");
        LambdaQueryWrapper<Repository> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Repository::getUserName,username).eq(Repository::getNameRepo,getNameRepo(username,repository));
        if (repositoryService.list(lambdaQueryWrapper).isEmpty()) DoopfsException.cast("当前仓库不存在");
    }



}
